package com.meilai.project.service.personnel.onTurn.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilai.project.dto.personnel.onTurn.EntryApplyCheckDTO;
import com.meilai.project.dto.personnel.onTurn.EntryApplyDTO;
import com.meilai.project.dto.personnel.onTurn.EntryListSearchDTO;
import com.meilai.project.dto.system.WebUserDTO;
import com.meilai.project.dto.system.WebUserDepartmentRelationDTO;
import com.meilai.project.dto.workflow.FlowBusinessDataSuperEntity;
import com.meilai.project.dto.workflow.WorkFlowCheckDTO;
import com.meilai.project.entity.personnel.onTurn.EntryApply;
import com.meilai.project.entity.personnel.onTurn.EntryApplyFamilyMember;
import com.meilai.project.entity.personnel.onTurn.EntryApplyWorkExperience;
import com.meilai.project.entity.personnel.org.Roster;
import com.meilai.project.entity.personnel.org.RosterFamilyMember;
import com.meilai.project.entity.personnel.org.RosterWorkExperience;
import com.meilai.project.entity.system.User;
import com.meilai.project.exception.CommonException;
import com.meilai.project.mapper.personnel.onTurn.EntryApplyMapper;
import com.meilai.project.mapper.personnel.org.DepartmentMapper;
import com.meilai.project.mapper.system.UserMapper;
import com.meilai.project.service.personnel.onTurn.EntryApplyFamilyMemberService;
import com.meilai.project.service.personnel.onTurn.EntryApplyService;
import com.meilai.project.service.personnel.onTurn.EntryApplyWorkExperienceService;
import com.meilai.project.service.personnel.org.RosterFamilyMemberService;
import com.meilai.project.service.personnel.org.RosterService;
import com.meilai.project.service.personnel.org.RosterWorkExperienceService;
import com.meilai.project.service.system.UserService;
import com.meilai.project.service.system.impl.UserServiceImpl;
import com.meilai.project.util.ThreadLocalUserUtil;
import com.meilai.project.util.WorkFlowUtil;
import com.meilai.project.vo.personnel.onTurn.EntryApplyFamilyMemberVO;
import com.meilai.project.vo.personnel.onTurn.EntryApplyVO;
import com.meilai.project.vo.personnel.onTurn.EntryApplyWorkExperienceVO;
import com.meilai.project.vo.system.UserForWFVO;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Log4j2
public class EntryApplyServiceImpl extends ServiceImpl<EntryApplyMapper, EntryApply> implements EntryApplyService {

    @Autowired
    private DepartmentMapper departmentMapper;
    @Autowired
    private EntryApplyFamilyMemberService entryApplyFamilyMemberService;
    @Autowired
    private EntryApplyWorkExperienceService entryApplyWorkExperienceService;
    @Autowired
    private RosterService rosterService;
    @Autowired
    private RosterFamilyMemberService rosterFamilyMemberService;
    @Autowired
    private RosterWorkExperienceService rosterWorkExperienceService;
    @Autowired
    private UserService userService;
    @Autowired
    private UserMapper userMapper;

    @Override
    public Page<EntryApplyVO> selectPageList(Integer current_page, Integer page_size, EntryListSearchDTO searchDTO) {
        Page<EntryApplyVO> page = new Page<EntryApplyVO>(current_page, page_size);
        List<EntryApplyVO> list = baseMapper.selectPageList(page, searchDTO, ThreadLocalUserUtil.get().getId());
        return page.setRecords(handleList(list));
    }

    // 二次处理list
    private List<EntryApplyVO> handleList(List<EntryApplyVO> originList) {
        if (CollectionUtils.isNotEmpty(originList)) {
            List<Long> ids = originList.stream().map(EntryApplyVO::getId).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(ids)) {

                HashMap<Long, List<EntryApplyFamilyMemberVO>> familyMemberMap = new HashMap<>();
                HashMap<Long, List<EntryApplyWorkExperienceVO>> workExperienceMap = new HashMap<>();

                List<EntryApplyFamilyMember> familyMembers = entryApplyFamilyMemberService.list(new QueryWrapper<EntryApplyFamilyMember>().in("pid", ids));
                if (CollectionUtils.isNotEmpty(familyMembers)) {
                    familyMembers.forEach(d -> {
                        EntryApplyFamilyMemberVO vo = new EntryApplyFamilyMemberVO();
                        BeanUtils.copyProperties(d, vo);
                        List<EntryApplyFamilyMemberVO> _list = familyMemberMap.getOrDefault(vo.getPid(), new ArrayList<>());
                        _list.add(vo);
                        familyMemberMap.put(d.getPid(), _list);
                    });
                }

                List<EntryApplyWorkExperience> workExperiences = entryApplyWorkExperienceService.list(new QueryWrapper<EntryApplyWorkExperience>().in("pid", ids));
                if (CollectionUtils.isNotEmpty(workExperiences)) {
                    workExperiences.forEach(d -> {
                        EntryApplyWorkExperienceVO vo = new EntryApplyWorkExperienceVO();
                        BeanUtils.copyProperties(d, vo);
                        List<EntryApplyWorkExperienceVO> _list = workExperienceMap.getOrDefault(vo.getPid(), new ArrayList<>());
                        _list.add(vo);
                        workExperienceMap.put(d.getPid(), _list);
                    });
                }

                originList.forEach(d -> {
                    d.setFamily_members(familyMemberMap.getOrDefault(d.getId(), new ArrayList<>()));
                    d.setWork_experiences(workExperienceMap.getOrDefault(d.getId(), new ArrayList<>()));
                });
            }
        }
        return originList;
    }

    @Override
    public EntryApplyVO getOneById(Long id) {
        EntryApply entryApply = getById(id);
        if (entryApply == null) throw new CommonException("数据不存在！");

        EntryApplyVO vo = new EntryApplyVO();
        BeanUtils.copyProperties(entryApply, vo);

        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();
        Boolean canCheck = WorkFlowUtil.canCheckData(userId, entryApply);
        vo.setCan_check(canCheck);
        vo.setIs_applyer(userId.equals(entryApply.getFlow_applyer_id()));

        List<EntryApplyVO> list = new ArrayList<>();
        list.add(vo);
        List<EntryApplyVO> _list = handleList(list);
        // 当前人是否是人事经理
        User user = userMapper.userIsARole(userId, "PersonnelManager");
        if (CollectionUtils.isNotEmpty(_list) && user != null && canCheck) {
            EntryApplyVO entryApplyVO = _list.get(0);
            List<String> can_update_field = Arrays.asList("comp_fill_entry_date", "comp_fill_belong_dep_id", "comp_fill_belong_dep", "comp_fill_job", "comp_fill_probation_start", "comp_fill_probation_end", "comp_fill_probation_sala", "comp_fill_salary");
            entryApplyVO.setCan_update_field(can_update_field);
        }
        return CollectionUtils.isNotEmpty(_list) ? _list.get(0) : null;
    }

    // 更新子表
    private boolean updateSlave(EntryApplyDTO dto, Long id) {
        entryApplyFamilyMemberService.remove(new QueryWrapper<EntryApplyFamilyMember>().eq("pid", id));
        entryApplyWorkExperienceService.remove(new QueryWrapper<EntryApplyWorkExperience>().eq("pid", id));
        if (CollectionUtils.isNotEmpty(dto.getFamily_members())) {
            List<EntryApplyFamilyMember> familyMembers = dto.getFamily_members().stream().filter(d -> {
                return StringUtils.isNotEmpty(d.getName()) && StringUtils.isNotEmpty(d.getCompany());
            }).map(d -> {
                EntryApplyFamilyMember temp = new EntryApplyFamilyMember();
                BeanUtils.copyProperties(d, temp);
                temp.setPid(id);
                return temp;
            }).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(familyMembers)) entryApplyFamilyMemberService.saveBatch(familyMembers);
        }
        if (CollectionUtils.isNotEmpty(dto.getWork_experiences())) {
            List<EntryApplyWorkExperience> workExperiences = dto.getWork_experiences().stream().filter(d -> {
                return StringUtils.isNotEmpty(d.getCompany());
            }).map(d -> {
                EntryApplyWorkExperience temp = new EntryApplyWorkExperience();
                BeanUtils.copyProperties(d, temp);
                temp.setPid(id);
                return temp;
            }).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(workExperiences)) entryApplyWorkExperienceService.saveBatch(workExperiences);
        }
        return true;
    }

    // 保存 和 提交 抽出相同代码
    private int saveAndBegin(EntryApplyDTO dto, boolean isSave) {
        // 获取当前人的id和部门id
        UserForWFVO user = UserServiceImpl.getUserForWF(dto);

        // 业务数据
        EntryApply temp = new EntryApply();
        BeanUtils.copyProperties(dto, temp);

        // 设置流程字段，flow_type请自行对应“dict_flow_type”表中的id， 比如当前入职流程（考察登记流程）的id是5
        String flow_title = dto.getName() + "的考察登记"; // 审批流程的名称，需要根据实际业务自行确定
        FlowBusinessDataSuperEntity fbdse = WorkFlowUtil.getStartSuperDataEntity(
                user.getUser_id(),  // 当前用户id
                user.getDep_id(), // 提交的部门id
                5, // 流程类型id，“dict_flow_type”表中的id
                flow_title // 自定义的流程标题
        );
        BeanUtils.copyProperties(fbdse, temp);

        // 是否为新数据
        // 新数据时，前端不传业务数据id
        // 非新数据时，即来源是“草稿箱”、“已删除”、“被退回”时，前端需要传入业务数据id
        boolean isFirst = (dto.getId() == null);

        // 由于流程字段含有id，会覆盖数据，此处需要手动setId
        if (!isFirst) temp.setId(dto.getId());

        // 新增或更新业务数据
        if (temp.getEntry_dep_id() != null)
            temp.setEntry_dep(departmentMapper.getDepFullNameById(temp.getEntry_dep_id()));
        if (temp.getComp_fill_belong_dep_id() != null)
            temp.setComp_fill_belong_dep(departmentMapper.getDepFullNameById(temp.getComp_fill_belong_dep_id()));
        boolean flag = (isFirst ? save(temp) : updateById(temp)) && updateSlave(dto, temp.getId());
        if (!flag) throw new CommonException("保存失败");

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<EntryApply, EntryApplyMapper>(temp.getId(), user.getUser_id(), EntryApply.class);

        // 审批流程id
        // 新数据,传0L
        // 非新数据,根据数据id,查找wfData_id
        Long wfData_id = isFirst ? 0L : wfu.getWfDataId(dto.getId());
        // 旧数据记录留存
        EntryApply old_data = isFirst ? new EntryApply() : temp;

        return isSave ? wfu.SaveDraft(wfData_id) : wfu.Begin(wfData_id, "", old_data);
    }

    // 通过 和 驳回 抽出相同代码
    private int passAndBack(EntryApplyCheckDTO dto, boolean isPass, boolean... updateData) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();

        // 若业务数据有审批中可修改的内容，请自行更新
        if (updateData != null && updateData[0]) {
            EntryApply updateTemp = new EntryApply();
            BeanUtils.copyProperties(dto, updateTemp);
            updateById(updateTemp);
        }

        // 业务数据
        EntryApply temp = getById(dto.getId());

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<EntryApply, EntryApplyMapper>(dto.getId(), userId, EntryApply.class);

        // 审批入参类
        WorkFlowCheckDTO checkDTO = WorkFlowCheckDTO.getWorkFlowCheckDTO(
                temp.getFlow_current_node_id(), // 当前节点id
                temp.getFlow_json(), // 流程json
                dto.getWf_check_opinion(), // 审批意见
                dto.getWf_check_sign_url(), // 审批签名链接
                JSONObject.toJSONString(temp) // 旧数据留存
        );

        // 审批
        return isPass ? wfu.ToNext(checkDTO) : wfu.ReturnBack(checkDTO);
    }

    // 删除 和 彻底删除 抽出相同代码
    private int deleteAndActualDel(Long id, boolean isActual) {
        // 当前人id
        Long userId = ThreadLocalUserUtil.get().getId();

        // 构造流程工具类
        WorkFlowUtil wfu = new WorkFlowUtil<EntryApply, EntryApplyMapper>(id, userId, EntryApply.class);

        // 流程id
        Long wfData_id = wfu.getWfDataId(id);

        return isActual ? wfu.ActualDelete(wfData_id) : wfu.Delete(wfData_id);
    }

    // 检验 新考察登记 联系电话全局唯一
    private boolean checkMobileUnique(String mobile) {
        boolean flag = true;
        List<User> users = userService.list(new QueryWrapper<User>().eq("mobile", mobile).isNull("deleted_at"));
        if (CollectionUtils.isNotEmpty(users)) flag = false;
        List<EntryApply> entryApplies = list(new QueryWrapper<EntryApply>().eq("mobile", mobile).isNull("deleted_at").in("flow_status", 3, 4));
        if (CollectionUtils.isNotEmpty(entryApplies)) flag = false;
        return flag;
    }

    @Override
    @Transactional
    public boolean save(EntryApplyDTO dto) {
        // 保存至草稿箱
        int flowResult = saveAndBegin(dto, true);
        if (flowResult != 1) throw new CommonException("保存至草稿箱失败，请联系管理员！");
        else return true;
    }

    @Override
    @Transactional
    public boolean begin(EntryApplyDTO dto) {
        if (!checkMobileUnique(dto.getMobile())) throw new CommonException("已存在相同手机的人员！");
        // 提交
        int flowResult = saveAndBegin(dto, false);
        if (flowResult == 1) {
            // 仍在流程中
            return true;
        } else if (flowResult == 2) {
            // 无审批节点，直接结束
            return true;
        } else {
            throw new CommonException("提交失败，请联系管理员！");
        }
    }

    @Override
    @Transactional
    public boolean pass(EntryApplyCheckDTO dto) {
        // 通过
        int flowResult = passAndBack(dto, true, false);
        if (flowResult == 1) {
            // 通过后仍然在流程中
            return true;
        } else if (flowResult == 2) {
            // 审核后流程结束
            // 保存至花名册
            EntryApply ea = getOneById(dto.getId());
            Roster roster = new Roster();
            BeanUtils.copyProperties(ea, roster);
            roster.setId(null);
            roster.setComp_fill_entry_date(null);


            // 保存花名册子表
            List<EntryApplyFamilyMember> eaFMs = entryApplyFamilyMemberService.list(new QueryWrapper<EntryApplyFamilyMember>().eq("pid", dto.getId()));
            if (CollectionUtils.isNotEmpty(eaFMs)) {
                List<RosterFamilyMember> familyMembers = eaFMs.stream().map(d -> {
                    RosterFamilyMember temp = new RosterFamilyMember();
                    BeanUtils.copyProperties(d, temp);
                    temp.setId(null);
                    temp.setPid(roster.getId());
                    return temp;
                }).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(familyMembers)) rosterFamilyMemberService.saveBatch(familyMembers);
            }
            List<EntryApplyWorkExperience> eaWEs = entryApplyWorkExperienceService.list(new QueryWrapper<EntryApplyWorkExperience>().eq("pid", dto.getId()));
            if (CollectionUtils.isNotEmpty(eaWEs)) {
                List<RosterWorkExperience> workExperiences = eaWEs.stream().map(d -> {
                    RosterWorkExperience temp = new RosterWorkExperience();
                    BeanUtils.copyProperties(d, temp);
                    temp.setId(null);
                    temp.setPid(roster.getId());
                    return temp;
                }).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(workExperiences)) rosterWorkExperienceService.saveBatch(workExperiences);
            }

            // 创建用户
            WebUserDTO addUser = new WebUserDTO();
            addUser.setName(ea.getName());
            addUser.setUsername(ea.getMobile());
            addUser.setMobile(ea.getMobile());
            addUser.setSex(ea.getSex());
            WebUserDepartmentRelationDTO dep = new WebUserDepartmentRelationDTO();
            dep.setId(ea.getEntry_dep_id());
            dep.setJob(ea.getEntry_job());
            dep.setMain(true);
            List<WebUserDepartmentRelationDTO> department_list = new ArrayList<>();
            department_list.add(dep);
            addUser.setDepartment_list(department_list);
            Long userId = userService.addOneReturnId(addUser);

            roster.setUser_id(userId);
            rosterService.save(roster);

            return true;
        } else {
            throw new CommonException("通过失败，请联系管理员！");
        }
    }

    @Override
    @Transactional
    public boolean returnBack(EntryApplyCheckDTO dto) {
        // 驳回
        int flowResult = passAndBack(dto, false, false);
        if (flowResult != 1) throw new CommonException("驳回失败，请联系管理员！");
        return true;
    }

    @Override
    @Transactional
    public boolean actualDelete(Long id) {
        int flowResult = deleteAndActualDel(id, true);
        if (flowResult != 1) throw new CommonException("彻底删除失败，请联系管理员！");
        return true;
    }

    @Override
    @Transactional
    public boolean delete(Long id) {
        int flowResult = deleteAndActualDel(id, false);
        if (flowResult != 1) throw new CommonException("删除失败，请联系管理员！");
        return true;
    }
}
